package za.org.jcicada.genss.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Abstract base class for threads for the state and mode state machines. Uses
 * Template Pattern to delegate functionality to derived implementations but
 * this class controls execution of the thread, in particular termination.
 */
public abstract class BaseRunner extends Thread {

    /** Logger object used for logging messages. */
    private static final Log logger = LogFactory.getLog(BaseRunner.class);
    
    /** Timeout period to wait for join to succeed. */
    public static final long JOIN_TIMEOUT = 1000;
    
    public BaseRunner(String name) {
    	super(name);
    }

    /**
     * Interrupts the thread and waits a indefinitely to join.
     */
    public void terminate() {
        terminate(0);
    }

    /**
     * Interrupts the thread and then waits to join.
     * 
     * @param timeout_ms maximum period to wait for join
     */
    public void terminate(long timeout_ms) {
        interrupt();
        try {
            this.join(timeout_ms);
        } catch (InterruptedException ex) {
            logger.error(String.format("Interrupted join of thread %s", getClass()));
        }
        if (isAlive()) {
            logger.warn(String.format("Failed to terminate thread %s", getClass()));
        }
    }

    /**
     * Worker method of thread repeats indefinitely until interrupted by a call
     * to terminate.
     */
    public void run() {
        logger.trace(String.format("Starting thread %s", getClass()));
        while (!isInterrupted()) {
            try {
                strategy();
            } catch (InterruptedException ex) {
                // break because interrupted flag cleared when exception
                // occurs
                break;
            }
        }
        logger.trace(String.format("Exiting thread %s", getClass()));
    }

    /**
     * Override this method to insert behaviour for this particular thread.
     */
    protected abstract void strategy() throws InterruptedException;

}
